/**
 * @file qp_bsp.c
 * @author your name (you@domain.com)
 * @brief
 * @version 0.1
 * @date 2024-07-27
 *
 * @copyright Copyright (c) 2024
 *
 */

#include "qp_bsp.h"
#include "dpp.h" // DPP Application interface
#include "qpc.h" // QP/C real-time embedded framework
#include "qp_port.h"

Q_DEFINE_THIS_FILE // define the name of this file for assertions

// RTOS 可以感知的优先级 (最低中断优先级可调用RTOS函数)
// "RTOS-aware" interrupt priorities for FreeRTOS on ARM Cortex-M, NOTE1
#define RTOS_AWARE_ISR_CMSIS_PRI                                               \
  (configMAX_SYSCALL_INTERRUPT_PRIORITY >> (8 - __NVIC_PRIO_BITS))

    // Local-scope objects -----------------------------------------------------
    static uint32_t l_rndSeed;

void BSP_randomSeed(uint32_t const seed);

/******************************其他与BSP硬件相关的函数移植*************************************************************/
#include "bsp.h"
#include "bsp_fmc_io.h"

#define BUTTON_KEY  (0)
extern uint8_t KeyPinActive(uint8_t _id);

uint32_t BSP_PB_GetState(uint8_t Button)
{
    return KeyPinActive(Button);
}

void BSP_LED_On(uint8_t _no)
{
    bsp_LedOn(_no);
}

void BSP_LED_Off(uint8_t _no)
{
bsp_LedOff(_no);
}



/********************************************************************************************/







/** 移植PQC必须自己自定义的函数 */
/**
 * @brief 错误处理函数
 *
 * @param module
 * @param id
 * @return Q_NORETURN
 */
Q_NORETURN Q_onError(char const *const module, int_t const id) {
  // NOTE: this implementation of the error handler is intended only
  // for debugging and MUST be changed for deployment of the application
  // (assuming that you ship your production code with assertions enabled).
  Q_UNUSED_PAR(module);
  Q_UNUSED_PAR(id);
  QS_ASSERTION(module, id, 10000U); // report assertion to QS

#ifndef NDEBUG
  // light up LED
  // BSP_LED_On(LED1);
  // for debugging, hang on in an endless loop...
  for (;;) {
  }
#else
  NVIC_SystemReset();
  for (;;) { // explicitly "no-return"
  }
#endif
}

// ISRs used in the application ==============================================
// NOTE: only the "FromISR" API variants are allowed in the ISRs!
// TODO: 目前使用的意义不明 暂时保留
void EXTI0_IRQHandler(void); // prototype
void EXTI0_IRQHandler(void) {
  BaseType_t xHigherPriorityTaskWoken = pdFALSE;

  // for testing...
  QACTIVE_POST_FROM_ISR(AO_Table, Q_NEW_FROM_ISR(QEvt, MAX_PUB_SIG),
                        &xHigherPriorityTaskWoken, &l_EXTI0_IRQHandler);

  // the usual end of FreeRTOS ISR...
  portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
}

// Application hooks used in this project ====================================
// NOTE: only the "FromISR" API variants are allowed in vApplicationTickHook
// TICK滴答时钟 钩子函数  系统时钟中断1ms 里面调用  必须短 不能阻塞 快进快出
void vApplicationTickHook(void) {
  BaseType_t xHigherPriorityTaskWoken = pdFALSE;

  // process time events at rate 0
  QTIMEEVT_TICK_FROM_ISR(0U, &xHigherPriorityTaskWoken, &l_TickHook);

  // Perform the debouncing of buttons. The algorithm for debouncing
  // adapted from the book "Embedded Systems Dictionary" by Jack Ganssle
  // and Michael Barr, page 71. 
  // 按键抖动消除算法
  static struct {
    uint32_t depressed;
    uint32_t previous;
  } buttons = {0U, 0U};

  uint32_t current = BSP_PB_GetState(BUTTON_KEY); // read the Key button
  uint32_t tmp = buttons.depressed; // save debounced depressed buttons
  buttons.depressed |= (buttons.previous & current); // set depressed
  buttons.depressed &= (buttons.previous | current); // clear released
  buttons.previous = current;                        // update the history
  tmp ^= buttons.depressed; // changed debounced depressed
  current = buttons.depressed;

  if (tmp != 0U) {       // debounced Key button state changed?
    if (current != 0U) { // is PB0 depressed?
      static QEvt const pauseEvt = QEVT_INITIALIZER(PAUSE_SIG);
      QACTIVE_PUBLISH_FROM_ISR(&pauseEvt, &xHigherPriorityTaskWoken,
                               &l_TickHook);
    } else { // the button is released
      static QEvt const serveEvt = QEVT_INITIALIZER(SERVE_SIG);
      QACTIVE_PUBLISH_FROM_ISR(&serveEvt, &xHigherPriorityTaskWoken,
                               &l_TickHook);
    }
  }

#ifdef Q_SPY
  tmp = SysTick->CTRL;            // clear SysTick_CTRL_COUNTFLAG
  QS_tickTime_ += QS_tickPeriod_; // account for the clock rollover
#endif

  // notify FreeRTOS to perform context switch from ISR, if needed
  portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
}

// 空闲任务钩子函数  每次运行空闲任务时调用
//............................................................................
void vApplicationIdleHook(void) {
    // toggle the User LED on and then off, see NOTE01
    QF_INT_DISABLE();
    BSP_LED_On (3);
    BSP_LED_Off(3);
    QF_INT_ENABLE();

    // Some floating point code is to exercise the VFP...
    double volatile x = 1.73205;
    x = x * 1.73205;

#ifdef Q_SPY
    QS_rxParse();  // parse all the received bytes

    if ((l_uartHandle.Instance->ISR & UART_FLAG_TXE) != 0U) { // TXE empty?
        QF_INT_DISABLE();
        uint16_t b = QS_getByte();
        QF_INT_ENABLE();

        if (b != QS_EOD) {  // not End-Of-Data?
            l_uartHandle.Instance->TDR = (b & 0xFFU);  // put into TDR
        }
    }
#elif defined NDEBUG
    // Put the CPU and peripherals to the low-power mode.
    // you might need to customize the clock management for your application,
    // see the datasheet for your particular Cortex-M MCU.
    //
    // !!!CAUTION!!!
    // The WFI instruction stops the CPU clock, which unfortunately disables
    // the JTAG port, so the ST-Link debugger can no longer connect to the
    // board. For that reason, the call to __WFI() has to be used with CAUTION.
    //
    // NOTE: If you find your board "frozen" like this, strap BOOT0 to VDD and
    // reset the board, then connect with ST-Link Utilities and erase the part.
    // The trick with BOOT(0) is it gets the part to run the System Loader
    // instead of your broken code. When done disconnect BOOT0, and start over.
    //
    //__WFI(); // Wait-For-Interrupt
#endif
}

//堆栈溢出钩子函数
//............................................................................
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) {
    (void)xTask;
    (void)pcTaskName;
    Q_ERROR();
}



static void qp_bsp_init(void) {

	// 硬件资源 3个LED 1个按键 1个随机数
    // Configure the LEDs
//    BSP_LED_Init(LED1);
//    BSP_LED_Init(LED2);
//    BSP_LED_Init(LED3);

    // configure the User Button in GPIO Mode
//    BSP_PB_Init(BUTTON_KEY, BUTTON_MODE_GPIO);

	//软件产生随机数
    BSP_randomSeed(1234U);

  // TODO:暂时不考虑QS相关
  // initialize the QS software tracing...
  //   if (!QS_INIT((void *)0)) {
  //     Q_ERROR();
  //   }

  //   // dictionaries...
  //   QS_OBJ_DICTIONARY(&l_TickHook);
  //   QS_OBJ_DICTIONARY(&l_EXTI0_IRQHandler);
  //   QS_USR_DICTIONARY(PHILO_STAT);
  //   QS_USR_DICTIONARY(PAUSED_STAT);

  //   QS_ONLY(produce_sig_dict());

  //   // setup the QS filters...
  //   QS_GLB_FILTER(QS_ALL_RECORDS); // all records
  //   QS_GLB_FILTER(-QS_QF_TICK);    // exclude the clock tick
}

static void qp_bsp_start(void) {
  // initialize event pools
  static QF_MPOOL_EL(TableEvt) smlPoolSto[2 * N_PHILO]; // small pool
  QF_poolInit(smlPoolSto, sizeof(smlPoolSto), sizeof(smlPoolSto[0]));

  // initialize publish-subscribe
  static QSubscrList subscrSto[MAX_PUB_SIG];
  QActive_psInit(subscrSto, Q_DIM(subscrSto));

  // start the active objects/threads...
  static QEvt const *philoQueueSto[N_PHILO][N_PHILO];
  static StackType_t philoStack[N_PHILO][configMINIMAL_STACK_SIZE];
  for (uint8_t n = 0U; n < N_PHILO; ++n) {
    Philo_ctor(n); // instantiate all Philosopher active objects
    QActive_setAttr(AO_Philo[n], TASK_NAME_ATTR, "Philo");
    QACTIVE_START(AO_Philo[n],             // AO to start
                  Q_PRIO(n + 3U, 3U),      // QP prio., FreeRTOS prio.
                  philoQueueSto[n],        // event queue storage
                  Q_DIM(philoQueueSto[n]), // queue length [events]
                  philoStack[n],           // stack storage
                  sizeof(philoStack[n]),   // stack size [bytes]
                  (QEvt *)0);              // initialization event (not used)
  }

  static QEvt const *tableQueueSto[N_PHILO];
  static StackType_t tableStack[configMINIMAL_STACK_SIZE];
  Table_ctor(); // instantiate the Table active object
  QActive_setAttr(AO_Table, TASK_NAME_ATTR, "Table");
  QACTIVE_START(AO_Table,                 // AO to start
                Q_PRIO(N_PHILO + 7U, 7U), // QP prio., FreeRTOS prio.
                tableQueueSto,            // event queue storage
                Q_DIM(tableQueueSto),     // queue length [events]
                tableStack,               // stack storage
                sizeof(tableStack),       // stack size [bytes]
                (QEvt *)0);               // initialization event (not used)
}


void BSP_displayPhilStat(uint8_t n, char const *stat) {
    Q_UNUSED_PAR(n);

    if (stat[0] == 'e') {
        BSP_LED_On(1);
    }
    else {
        BSP_LED_Off(1);
    }

    // app-specific trace record...
    QS_BEGIN_ID(PHILO_STAT, AO_Table->prio)
        QS_U8(1, n);  // Philosopher number
        QS_STR(stat); // Philosopher status
    QS_END()
}

//............................................................................
void BSP_displayPaused(uint8_t const paused) {
    if (paused != 0U) {
        BSP_LED_On(2);
    }
    else {
        BSP_LED_Off(2);
    }

    // application-specific trace record
    QS_BEGIN_ID(PAUSED_STAT, AO_Table->prio)
        QS_U8(1, paused);  // Paused status
    QS_END()
}

//............................................................................
void BSP_randomSeed(uint32_t const seed) {
    l_rndSeed = seed;
}
//............................................................................
// 线性同余产生随机数
uint32_t BSP_random(void) { // a very cheap pseudo-random-number generator
    // Some floating point code is to exercise the VFP...
    double volatile x = 3.1415926;
    x = x + 2.7182818;

    vTaskSuspendAll(); // lock FreeRTOS scheduler
    // "Super-Duper" Linear Congruential Generator (LCG)
    // LCG(2^32, 3*7*11*13*23, 0, seed)
    //
    uint32_t rnd = l_rndSeed * (3U*7U*11U*13U*23U);
    l_rndSeed = rnd; // set for the next time
    xTaskResumeAll(); // unlock the FreeRTOS scheduler

    return (rnd >> 8);
}
//............................................................................
void BSP_ledOn(void) {
    BSP_LED_On(3);
}
//............................................................................
void BSP_ledOff(void) {
    BSP_LED_Off(3);
}
//............................................................................
void BSP_terminate(int16_t result) {
    Q_UNUSED_PAR(result);
}


/**
 * @brief
 * 在 QF 返回底层操作系统或 RTOS 前，调用
QF_onCleanup(void)。
QF_onCleanup(void)回调函数的意图是给应用程序一个机会，让它在退出之前执行
清理工作。如果特定的应用程序没有要清理的或应用程序从不返回，这个函数也许是空函数。
 *
 */
/**
 * @brief 清除函数
 *
 */
void QF_onCleanup(void) {}

// QF callbacks --------------------------------------------------------------

/**
 * QF_onStartup()回调函数的主要意图是初始化并
启动中断。调用 QF_onStartup()的时间取决于特定的 QF
移植。然而，在绝大多数情况下，刚好在启 动任何多任务内核或其他后台循环前，从
QF_run() 里调用 QF_onStartup()。
 *
 */
void QF_onStartup(void) {
  // TODO:待完善框架
  //      // set up the SysTick timer to fire at BSP_TICKS_PER_SEC rate
  //      //SysTick_Config(SystemCoreClock / BSP_TICKS_PER_SEC); // done in
  //      FreeRTOS

  //     // assign all priority bits for preemption-prio. and none to sub-prio.
  //     NVIC_SetPriorityGrouping(0U);

  //     // set priorities of ALL ISRs used in the system, see NOTE1
  //     NVIC_SetPriority(USART3_IRQn,    0U); // kernel unaware interrupt
  //     NVIC_SetPriority(EXTI0_IRQn,     RTOS_AWARE_ISR_CMSIS_PRI + 0U);
  //     NVIC_SetPriority(SysTick_IRQn,   RTOS_AWARE_ISR_CMSIS_PRI + 1U);
  //     // ...

  //     // enable IRQs...
  //     NVIC_EnableIRQ(EXTI0_IRQn);

  // #ifdef Q_SPY
  //     NVIC_EnableIRQ(USART3_IRQn); // UART3 interrupt used for QS-RX
  // #endif
}

void qpc_bsp_init(void) {
  QF_init();      // initialize the framework and the underlying RT kernel
  qp_bsp_init();  // initialize the BSP
  qp_bsp_start(); // start the AOs/Threads
  QF_run(); // run the QF application
}


//============================================================================
// NOTE1:
// The configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY constant from the
// FreeRTOS configuration file specifies the highest ISR priority that
// is disabled by the QF framework. The value is suitable for the
// NVIC_SetPriority() CMSIS function.
//
// Only ISRs prioritized at or below the
// configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY level (i.e.,
// with the numerical values of priorities equal or higher than
// configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY) are allowed to call any
// QP/FreeRTOS services. These ISRs are "kernel-aware".
//
// Conversely, any ISRs prioritized above the
// configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY priority level (i.e., with
// the numerical values of priorities less than
// configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY) are never disabled and are
// not aware of the kernel. Such "kernel-unaware" ISRs cannot call any
// QP/FreeRTOS services. The only mechanism by which a "kernel-unaware" ISR
// can communicate with the QF framework is by triggering a "kernel-aware"
// ISR, which can post/publish events.
//
// For more information, see article "Running the RTOS on a ARM Cortex-M Core"
// http://www.freertos.org/RTOS-Cortex-M3-M4.html
//
// NOTE2:
// The User LED is used to visualize the idle loop activity. The brightness
// of the LED is proportional to the frequency of invocations of the idle loop.
// Please note that the LED is toggled with interrupts locked, so no interrupt
// execution time contributes to the brightness of the User LED.

// NOTE1：
// FreeRTOS 配置文件中的 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 常量指定了 ISR 的最高优先级。
// FreeRTOS 配置文件中的 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 常量指定了
// QF 框架禁用的最高 ISR 优先级。该值适用于
// NVIC_SetPriority() CMSIS 函数。
//
// 只有优先级等于或低于
// 只有优先级等于或低于 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 级别的 ISR（即
// 优先级数值等于或高于
// configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY）允许调用任何
// QP/FreeRTOS 服务。这些 ISR 是 “内核感知 ”的。
//
// 相反，优先级高于
// configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 优先级以上的 ISR（即优先级数值小于
// 优先级的数值小于
// configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY ）永远不会被禁用，也不会被内核感知。
// 无法感知内核。这种 “内核不感知 ”ISR 不能调用任何
// QP/FreeRTOS 服务。未感知内核 "ISR 与 QF 框架通信的唯一机制是
// 与 QF 框架通信的唯一机制是触发 "内核感知”
// ISR 可以发布/公布事件。
//
// 更多信息，请参阅 “在 ARM Cortex-M 内核上运行 RTOS” 一文。
// http://www.freertos.org/RTOS-Cortex-M3-M4.html


// NOTE2：
// 用户 LED 用于显示空闲循环活动。LED 的亮度
// LED 的亮度与调用空闲循环的频率成正比。
// 请注意，LED 指示灯是在锁定中断的情况下切换的，因此中断执行时间不会对 LED 指示灯的亮度产生影响。
// 执行时间不会影响用户 LED 指示灯的亮度。
